{***************************************************************************}
{                                                                           }
{           Spring Framework for Delphi                                     }
{                                                                           }
{           Copyright (c) 2009-2013 Spring4D Team                           }
{                                                                           }
{           http://www.spring4d.org                                         }
{                                                                           }
{***************************************************************************}
{                                                                           }
{  Licensed under the Apache License, Version 2.0 (the "License");          }
{  you may not use this file except in compliance with the License.         }
{  You may obtain a copy of the License at                                  }
{                                                                           }
{      http://www.apache.org/licenses/LICENSE-2.0                           }
{                                                                           }
{  Unless required by applicable law or agreed to in writing, software      }
{  distributed under the License is distributed on an "AS IS" BASIS,        }
{  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. }
{  See the License for the specific language governing permissions and      }
{  limitations under the License.                                           }
{                                                                           }
{***************************************************************************}

unit Spring.Collections.Queues;

interface

uses
  Generics.Collections,
  Spring.Collections,
  Spring.Collections.Base;

type
  TQueue<T> = class(TEnumerableBase<T>, IQueue<T>)
  private
    type
      TGenericQueue = Generics.Collections.TQueue<T>;
  private
    fQueue: TGenericQueue;
    fOnNotify: ICollectionNotifyEvent<T>;
    function GetOnNotify: ICollectionNotifyEvent<T>;
  protected
    function GetCount: Integer; override;
    procedure Notify(const item: T; action: TCollectionNotification); virtual;
  public
    constructor Create; overload; override;
    constructor Create(const collection: array of T); overload;
    constructor Create(const collection: IEnumerable<T>); overload;

    destructor Destroy; override;

    function GetEnumerator: IEnumerator<T>; override;

    procedure Clear;

    procedure Enqueue(const item: T);
    function Dequeue: T;
    function Peek: T;
    function PeekOrDefault: T;
    function TryPeek(out item: T): Boolean;
    procedure TrimExcess;

    function AsQueue: Queue<T>;

    property OnNotify: ICollectionNotifyEvent<T> read GetOnNotify;
  end;

  TObjectQueue<T: class> = class(TQueue<T>)
  private
    FOwnsObjects: Boolean;
  protected
    procedure Notify(const item: T; action: TCollectionNotification); override;
  public
    constructor Create(ownsObjects: Boolean = True); overload;
    constructor Create(const collection: array of T; ownsObjects: Boolean = True); overload;
    constructor Create(const collection: IEnumerable<T>; ownsObjects: Boolean = True); overload;

    property OwnsObjects: Boolean read FOwnsObjects write FOwnsObjects;
  end;


implementation

uses
  Spring.Collections.Events,
  Spring.Collections.Extensions;


{$REGION 'TQueue<T>'}

constructor TQueue<T>.Create;
begin
  inherited Create;
  fQueue := TGenericQueue.Create;
  fOnNotify := TCollectionNotifyEventImpl<T>.Create;
end;

constructor TQueue<T>.Create(const collection: array of T);
var
  item: T;
begin
  Create;
  for item in collection do
  begin
    Enqueue(item);
  end;
end;

constructor TQueue<T>.Create(const collection: IEnumerable<T>);
var
  item: T;
begin
  Create;
  for item in collection do
  begin
    Enqueue(item);
  end;
end;

destructor TQueue<T>.Destroy;
begin
  // call our Clear to trigger Notify
  Clear;
  fQueue.Free;

  inherited Destroy;
end;

function TQueue<T>.GetEnumerator: IEnumerator<T>;
begin
  Result := TEnumeratorAdapter<T>.Create(fQueue);
end;

procedure TQueue<T>.Enqueue(const item: T);
begin
  fQueue.Enqueue(item);

  Notify(item, cnAdded);
end;

function TQueue<T>.Dequeue: T;
begin
  Result := fQueue.Dequeue;

  Notify(Result, cnRemoved);
end;

function TQueue<T>.AsQueue: Queue<T>;
begin
  Result := Self;
end;

procedure TQueue<T>.Clear;
begin
  // do not call fStack.Clear because we want our Notify to be triggered.
  while Count > 0 do
    Dequeue;
  TrimExcess;
end;

function TQueue<T>.Peek: T;
begin
  Result := fQueue.Peek;
end;

function TQueue<T>.PeekOrDefault: T;
begin
  if fQueue.Count > 0 then
    Result := fQueue.Peek
  else
    Result := Default(T);
end;

procedure TQueue<T>.TrimExcess;
begin
  fQueue.TrimExcess;
end;

function TQueue<T>.TryPeek(out item: T): Boolean;
begin
  Result := fQueue.Count > 0;
  if Result then
    item := fQueue.Peek
  else
    item := Default(T);
end;

function TQueue<T>.GetCount: Integer;
begin
  Result := fQueue.Count;
end;

function TQueue<T>.GetOnNotify: ICollectionNotifyEvent<T>;
begin
  Result := fOnNotify;
end;

procedure TQueue<T>.Notify(const item: T; action: TCollectionNotification);
begin
  if not fOnNotify.IsEmpty and fOnNotify.Enabled then
  begin
    fOnNotify.Invoke(Self, item, action);
  end;
end;

{$ENDREGION}


{$REGION 'TObjectQueue<T>'}

constructor TObjectQueue<T>.Create(ownsObjects: Boolean);
begin
  inherited Create;
  fOwnsObjects := ownsObjects;
end;

constructor TObjectQueue<T>.Create(const collection: array of T;
  ownsObjects: Boolean);
begin
  inherited Create(collection);
  fOwnsObjects := ownsObjects;
end;

constructor TObjectQueue<T>.Create(const collection: IEnumerable<T>;
  ownsObjects: Boolean);
begin
  inherited Create(collection);
  fOwnsObjects := ownsObjects;
end;

procedure TObjectQueue<T>.Notify(const item: T;
  action: TCollectionNotification);
begin
  inherited;
  if fOwnsObjects and (Action = cnRemoved) then
    item.Free;
end;

{$ENDREGION}


end.
